package com.blue.http.server;

import java.io.InputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.task.TaskExecutor;

import com.blue.core.file.ClassScanner;
import com.blue.core.util.FileUtil;
import com.blue.core.util.NumberUtil;
import com.blue.http.annotation.HttpClassHandler;
import com.blue.http.annotation.HttpFilterParser;
import com.blue.http.annotation.HttpParser;
import com.blue.http.invoke.Invoker;
import com.blue.http.invoke.UploadInvokerFilter;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpServerKeepAliveHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;

/**
 * HTTP初始化器
 * 
 * @author zhengj
 * @since 1.0 2017年3月14日
 */
public class HttpInitializer extends ChannelInitializer<SocketChannel> implements InitializingBean, ApplicationContextAware
{
	private static Logger logger = LoggerFactory.getLogger(HttpInitializer.class);

	private long maxUploadSize;
	private TaskExecutor taskExecutor;
	private ApplicationContext ctx;
	private List<String> packageList;
	private List<Class<?>> filterList;
	private HttpParser httpParser;
	private HttpFilterParser httpFilterParser;

	// SSL
	private SslContext sslContext;
	private boolean ssl = false;
	private String jks;
	private String pwd;
	private String alias;

	public HttpInitializer()
	{
	}

	@Override
	protected void initChannel(SocketChannel ch) throws Exception
	{
		Invoker invoker = new Invoker(httpParser, httpFilterParser);
		ChannelPipeline cp = ch.pipeline();

		if (sslContext != null)
		{
			cp.addLast(sslContext.newHandler(ch.alloc()));
		}

		cp.addLast(new HttpServerCodec());
		cp.addLast(new HttpServerKeepAliveHandler());
		cp.addLast(new HttpObjectAggregator(Integer.MAX_VALUE));
		cp.addLast(new HttpServerHandler(taskExecutor, invoker, httpParser, maxUploadSize));
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
	{
		ctx = applicationContext;
	}

	@Override
	public void afterPropertiesSet() throws Exception
	{
		httpParser = new HttpParser(ctx);

		logger.info("扫描包：{}", packageList);
		ClassScanner scanner = new ClassScanner();
		scanner.setClassHandler(new HttpClassHandler(httpParser));
		scanner.scan(true, packageList);

		httpFilterParser = new HttpFilterParser(ctx);
		httpFilterParser.parse(UploadInvokerFilter.class);
		if (filterList != null && !filterList.isEmpty())
		{
			for (Class<?> filter : filterList)
			{
				httpFilterParser.parse(filter);
			}
		}

		logger.info("最大文件上传大小：{}", maxUploadSize > 0 ? NumberUtil.byteFormat(maxUploadSize) : "无限制");
		logger.info("{}支持 SSL 通讯", ssl ? "" : "不");
		this.initCert();
	}
	
	private void initCert()
	{
		if (!ssl)
			return;

		try
		{
			if (jks == null || jks.isEmpty())
			{
				SelfSignedCertificate ssc = new SelfSignedCertificate();
				sslContext = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
				logger.info("使用 Netty 自行颁发的证书");
			}
			else
			{
				char[] passwd = pwd == null ? null : pwd.toCharArray();
				KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
				try (InputStream is = FileUtil.toInputStream(jks))
				{
					ks.load(is, passwd);
				}
				PrivateKey privateKey = (PrivateKey)ks.getKey(alias, passwd);
				X509Certificate x509 = (X509Certificate)ks.getCertificate(alias);
				sslContext = SslContextBuilder.forServer(privateKey, x509).build();
				logger.info("使用证书：{}", jks);
			}
		}
		catch (Exception e)
		{
			logger.error("创建证书失败：", e);
		}
	}

	public void setSsl(boolean ssl)
	{
		this.ssl = ssl;
	}

	public void setPwd(String pwd)
	{
		this.pwd = pwd;
	}

	public void setJks(String jks)
	{
		this.jks = jks;
	}

	public void setAlias(String alias)
	{
		this.alias = alias;
	}

	public void setFilters(List<Class<?>> filterList)
	{
		this.filterList = filterList;
	}

	public void setScanPackages(List<String> packageList)
	{
		this.packageList = packageList;
	}

	public void setTaskExecutor(TaskExecutor taskExecutor)
	{
		this.taskExecutor = taskExecutor;
	}

	public void setMaxUploadSize(long maxUploadSize)
	{
		this.maxUploadSize = maxUploadSize;
	}

}
